////////////////////////////////////////////////////////////////////////////////////
// File:        EsetManager.cpp
// Purpose:     Implementation of EsetManager Class
// Author:      Tukero
// Copyright:   (c) Tukero <tukero@gmail.com>
// Licence:     GNU/LGPL v3
////////////////////////////////////////////////////////////////////////////////////

#include "ESETManager.h"
#include "Utils.h"
#include "wx/tkRegKey.h"
#include "Utils.h"
#include "Settings.h"

#define DONTWANT_RunNonElevated
#define NO_DLL_IMPORTS
#include "VistaTools.cxx"

EsetManager::EsetManager() : EsetInfo()
{
    m_Profile.Empty();
    m_ProfilePath.Empty();
    m_RestoreActiveWindow = true;
}
bool EsetManager::IsEsetInstalled() const
{
    return (m_NodState != NOD_ISNOTINSTALLED);
}

// ---------------------------------------------------------------------------
// Updats the path of the current license. 
// license2Insert: when the path is ok, this license will be inserted
// ---------------------------------------------------------------------------
void EsetManager::UpdatePath(License *license2Insert, bool* inserted)
{    
    wxString PathOfReg;
    wxString profilePath = GetProfileSettingsPath();

    tkRegKey RegKey(profilePath);
    if (m_NodState == NOD_OLDVERSION)
    {
        if (RegKey.Exists() && !profilePath.IsEmpty())
        {
            if (license2Insert == NULL)
                return;
            else
                SetLicenseInRegistry(profilePath, license2Insert);
        }
        else 
        {
            PathOfReg = wxT("HKEY_LOCAL_MACHINE\\SOFTWARE\\ESET\\NOD\\CurrentVersion\\Modules\\Update\\Settings");
            RegKey.OpenKey(PathOfReg);
            if (RegKey.Exists())
            {
                int c;                                     //counter
                wxString Config;                           //Saves the Key-Name to compare
                wxString Profile;                          //Saves the Profile-Name of the Key-Name to compare
                m_Profile.Empty();
                
                if (RegKey.HasValue(wxT("PROFILE_CURRENTPROFILE")))           //Para verificar si tiene esa clave, generalmente en instalaciones nuevas no la tiene
                    RegKey.QueryValue(wxT("PROFILE_CURRENTPROFILE"),m_Profile);

                //Generates Key-Names from Config000 to Config999 to find the profile name used in nod32
                for (c=0;c<=999;c++)
                {
                    Config.Printf(wxT("Config%03d"),c);                        //Generates the KeyName string 
                    
                    RegKey.OpenKey(PathOfReg + wxT("\\") + Config);                
                    if (RegKey.HasValue(wxT("PROFILE_NAME")))                 //Checks if the Key has an item called "PROFILE_NAME"
                    {
                        RegKey.QueryValue(wxT("PROFILE_NAME"),Profile);       //If the key had the item, this key is the correct
                        if (m_Profile.CmpNoCase(Profile) == 0 || m_Profile.IsEmpty())
                        {                        
                            if (m_Profile.IsEmpty()) m_Profile = Profile;
                            PathOfReg.Append(wxT("\\") + Config);
                            PathOfReg.Append(wxT("\\Settings"));

                            m_ProfilePath = PathOfReg;                    //Saves the path of the key

                            if (license2Insert != NULL)
                            {
                                RegKey.OpenKey(m_ProfilePath);
                                bool elevated = !IsVista() || (IsVista() && IsElevated() == S_OK);
                                if (!RegKey.Exists() && elevated)                                
                                    RegKey.Create();

                                bool isLicenseInserted = SetLicenseInRegistry(m_ProfilePath, license2Insert);
                                if (inserted != NULL)
                                    *inserted = isLicenseInserted;
                            }
                            break;                                        
                        }
                    }
                }
            }
        }
    }
    else if (m_NodState == NOD_NEWVERSION)
    {
        if (RegKey.Exists() && !profilePath.IsEmpty())
            return;

        PathOfReg = wxT("HKEY_LOCAL_MACHINE\\SOFTWARE\\ESET\\ESET Security\\CurrentVersion\\Plugins\\01000400\\Profiles");
        RegKey.OpenKey(PathOfReg);

        if (RegKey.Exists())
        {               
            if (RegKey.HasValue(wxT("Active")))
                RegKey.QueryValue(wxT("Active"),m_Profile);                      //gets current profile
          
            PathOfReg.Append(wxT("\\") + m_Profile);                              //appends current profile name to full path registry                    
            RegKey.OpenKey(PathOfReg);
            if (RegKey.Exists() && m_Profile.Len() > 0)
                m_ProfilePath = PathOfReg;                                   //saves profile settings path 
        }
    }
}
// ---------------------------------------------------------------------------
// Saves the license in the registry
// ---------------------------------------------------------------------------
bool EsetManager::SetLicenseInRegistry(wxString settingsPath, License *license2Insert)
{
    bool elevated = !IsVista() || (IsVista() && IsElevated() == S_OK);
    if (license2Insert != NULL && elevated == true && !settingsPath.IsEmpty())
    {
        tkRegKey RegKey(settingsPath);
        if (RegKey.Exists())
        {
            RegKey.SetValue(wxT("Username"),license2Insert->GetUsername());

            wxMemoryBuffer binPass;
            License::EncodePass(license2Insert->GetPassword(),binPass);
            RegKey.SetValue(wxT("Password"),binPass);

            return true;
        }
    }
    return false;
}

// ---------------------------------------------------------------------------
// Loads all ESET information. Current version, version type, profile path
// ---------------------------------------------------------------------------
void EsetManager::LoadInfo()
{    
    m_NodState = NOD_ISNOTINSTALLED;//for default, nod32 is not installed

    wxString Version;             //nod32 version in text format
    wxString Language;            //to save "Language" registry key
    wxString PathOfReg;           //to save the path of the current profile
    long Build;                   //to save build (part of nod32 version)

    m_ProfilePath.Empty();
    m_VersionType = wxT("eav");
    tkRegKey RegKey;

    //wxLogError(wxT("HKEY_LOCAL_MACHINE\\SOFTWARE\\ESET\\NOD\\CurrentVersion\\Info"));
    RegKey.OpenKey(wxT("HKEY_LOCAL_MACHINE\\SOFTWARE\\ESET\\NOD\\CurrentVersion\\Info"));
    if (RegKey.HasValue(wxT("CurrentVersion")))            //if the registry has this value, nod32 can be installed
    {
        RegKey.QueryValue(wxT("CurrentVersion"),Version);
        //wxLogError(wxT("CurrentVersion old: %s"),Version);
        if (Version.Len())                             //check if the "version" key has some value
        {
            //wxLogError(wxT("OLD VERSION"));
            m_NodState=NOD_OLDVERSION;                 //nod32 2.7x is installed
            RegKey.QueryValue(wxT("Language"),Language);   //save the nod32 Language, it is necessary for get profile path

            RegKey.OpenKey(wxT("HKEY_LOCAL_MACHINE\\SOFTWARE\\ESET\\NOD\\CurrentVersion\\InstalledComponents\\NOD32MOD_WINNT_") + Language + wxT("_BASE"));

            RegKey.QueryValue(wxT("Build"),&Build);        //save the version in "build" variable
            //ConvertVersion(Build,m_NodVersion);        //convertimos la version mediante esta funcion, a a un formato mas entendible (separando cada parte que conforma la version)
            m_EsetVersion.SetFromConversion( Build );
        }
    }
    if (m_NodState != NOD_OLDVERSION)
    {
        //wxLogError(wxT("HKEY_LOCAL_MACHINE\\SOFTWARE\\ESET\\ESET Security\\CurrentVersion\\Info"));
        RegKey.OpenKey(wxT("HKEY_LOCAL_MACHINE\\SOFTWARE\\ESET\\ESET Security\\CurrentVersion\\Info"));

        if (RegKey.HasValue(wxT("ProductVersion")))
        {
            
            RegKey.QueryValue(wxT("ProductVersion"),Version); //Saves the Version string (it has the format: ###.###.###)
            //wxLogError(wxT("ProductVersion new: %s"),Version);
            if (Version.Len())
            {
                //wxLogError(wxT("NEW VERSION"),Version);
                m_NodState = NOD_NEWVERSION;                                    //Nod32 3.x or superior is installed

                wxStringTokenizer versionParts(Version, wxT("."));   //separes the version in parts

                versionParts.GetNextToken().ToLong(&m_EsetVersion.m_Major);  //gets the first part
                versionParts.GetNextToken().ToLong(&m_EsetVersion.m_Minor);  //  the second
                versionParts.GetNextToken().ToLong(&m_EsetVersion.m_Release);//  third
                versionParts.GetNextToken().ToLong(&m_EsetVersion.m_Build);  //  nad the last part
                
                /*
                wxRegEx RegEx;                
                
                RegEx.Compile(wxT("(^[0-9]+)\\.([0-9]+)\\.([0-9]+)\\.([0-9]+)"));
                if ( RegEx.Matches(Version) ) 
                {
                    RegEx.GetMatch(Version, 1).ToLong(&m_NodVersion.Major);
                    RegEx.GetMatch(Version, 2).ToLong(&m_NodVersion.Minor);      
                    RegEx.GetMatch(Version, 3).ToLong(&m_NodVersion.Release);
                    RegEx.GetMatch(Version, 4).ToLong(&m_NodVersion.Build);
                }
                */
                if (RegKey.HasValue(wxT("ProductType")))
                {
                    RegKey.QueryValue(wxT("ProductType"),m_VersionType);                 //Saves the type of installed (ess, essbe, eav) version
                    m_VersionType.MakeLower();
                }
                else
                    m_VersionType = wxT("eav");

                if (RegKey.HasValue(wxT("LanguageId")))
                    RegKey.QueryValue(wxT("LanguageId"),&m_LanguageId);
                
            }
        }
    }
    if (m_NodState != NOD_ISNOTINSTALLED && m_VersionType.Len() < 1)
        m_VersionType = wxT("eav");

    UpdatePath();
}
// ---------------------------------------------------------------------------
// Returns the path of the current profile used in ESET
// ---------------------------------------------------------------------------
wxString EsetManager::GetProfileSettingsPath()
{
    if (m_ProfilePath.Len() == 0 || m_NodState == NOD_ISNOTINSTALLED) 
        return wxT("");

    wxString settingsPath = m_ProfilePath;
    return settingsPath;
}

//
// Returns the setting value from the registry, requires the setting path.
//
wxString EsetManager::GetProfileSetting(wxString section)
{
    UpdatePath();
    wxString settingsPath = GetProfileSettingsPath();
    wxString value = wxT("");
    if (settingsPath.Len() == 0) 
        return wxT("");
   
    tkRegKey RegKey(settingsPath);
    if (RegKey.Exists() && RegKey.HasValue(section))
    {
        if (RegKey.GetValueType(section)== tkRegKey::Type_Binary)
        {   //converts from binary type to string type
            wxMemoryBuffer binValue;
            RegKey.QueryValue(section,binValue);

            wxChar *data = (wxChar *)binValue.GetData();
            int dataLen = binValue.GetDataLen() / sizeof(wxChar);
            value = *(new wxString(data,dataLen));
        }
        else
            RegKey.QueryValue(section,value);
    }

    return value;
}

// --------------------------------------------------------------------------
// Returns the username from registry
// --------------------------------------------------------------------------
wxString EsetManager::GetUsername()
{
    return GetProfileSetting(wxT("Username"));
}

// ---------------------------------------------------------------------------
// Returns the key used to unlock the eset settings
// ---------------------------------------------------------------------------
wxString EsetManager::GetKey2Unlock()
{
    return Utils::DecodeBlowfish( SETTINGS->GetKeyUnlock() );
    //wxConfig config;
    //return Utils::DecodeBlowfish(config.Read(wxT("KeyUnlock")));
}


// --------------------------------------------------------------------------
// Returns the password
// --------------------------------------------------------------------------
wxString EsetManager::GetPassword()
{
    return GetProfileSetting(wxT("Password"));
}

// ----------------------------------------------------------------------------
// Returns true if the given license is the same than the current used in ESET
// ----------------------------------------------------------------------------
bool EsetManager::IsCurrentLicenseSameAs(License& license)
{
    wxString binStrPass;
    License::EncodePass(license.GetPassword(),binStrPass);
    if (binStrPass == GetPassword() && GetUsername() == license.GetUsername())
        return true;
    else 
        return false;
}

int EsetManager::UnlockWithPass(HWND& nodHandler, HWND& dlgHandle, HWND& containerHandle, wxThread *thread)
{
    HWND dataHandle;
    HandlesArray handles;

    bool useKey = SETTINGS->UseKey();
    wxString key2Unlock = GetKey2Unlock();

    if (useKey == true && !key2Unlock.IsEmpty())
    {
        dataHandle = FindWindowEx(dlgHandle,0,wxT("Edit"),NULL);

        Utils::getHandlesList(nodHandler,handles);                
        
        SendMessage(dataHandle,WM_SETTEXT,0,(LPARAM)key2Unlock.c_str());
        SendMessage(dlgHandle, WM_IME_KEYDOWN, VK_RETURN, 0);
        
        thSleep(60,thread)
        //tkSleep(60);

        HWND errDlgHandle=NULL;
        do
        {
            errDlgHandle=FindWindowEx(0,errDlgHandle,wxT("#32770"),0);
        }while(errDlgHandle != NULL && GetParent(errDlgHandle) != dlgHandle);                                
        if (errDlgHandle != NULL)
        {
            SendMessage(errDlgHandle, WM_IME_KEYDOWN, VK_ESCAPE, 0);       //closes the Error dialog
            SendMessage(dlgHandle, WM_IME_KEYDOWN, VK_ESCAPE, 0); //closes the Settings Protection dialog
            return 4;
        }
        else
        {                
            dlgHandle = Utils::getDlgHandle(nodHandler,wxT("#32770"),handles,thread);
            if (m_EsetVersion.m_Major == 3)
                containerHandle = dlgHandle;
            else
                containerHandle = FindWindowEx(dlgHandle,0,wxT("#32770"),0);

            return 0;
        }
    }
    else
    {        
        SendMessage(dlgHandle, WM_IME_KEYDOWN, VK_ESCAPE, 0);    //closes the Password Dialog and shows a message
        return 6;
    }

}

int EsetManager::InsertLicense(License& license, wxThread *thread)
{
    if (IsCurrentLicenseSameAs(license))
        return 1;

    if (m_NodState == NOD_NEWVERSION)//for versions >= 3
    {
        HWND activeWindow = GetForegroundWindow();
	    HWND nodHandler = FindWindow(wxT("ESET Client Frame"),0);
        HWND dlgHandle = NULL;
        HWND dataHandle = NULL;
        HWND containerHandle = NULL;
        HandlesArray handles;

        if ( nodHandler == NULL )
            return 2;

        Utils::getHandlesList(nodHandler,handles);
        SetLastError(0);

        if ( (m_EsetVersion.m_Major == 4 && m_EsetVersion.m_Minor >= 2) || m_EsetVersion.m_Major > 4)
            PostMessage(nodHandler,WM_COMMAND,0x18023,0); //simulate clic to menu with id 0x8093 (ID in hex of the Menu "username and password setup")      
        else
            PostMessage(nodHandler,WM_COMMAND,0x18093,0); //simulate clic to menu with id 0x8093 (ID in hex of the Menu "username and password setup")      

        
        if (GetLastError() == 5) //access denied
        {
            return 7;
        }
        dlgHandle = Utils::getDlgHandle(nodHandler,wxT("#32770"),handles,thread);

        if (dlgHandle == NULL) 
            return 3;
        
        if (m_EsetVersion.m_Major == 3)
        {   //if the version of nod32 is 3.x, the license dialog contains two textbox and none panel
            dataHandle = FindWindowEx(dlgHandle,0,wxT("Edit"),NULL);
            dataHandle = FindWindowEx(dlgHandle,dataHandle,wxT("Edit"),NULL);
            if (dataHandle == NULL) 
            {
                int err = UnlockWithPass(nodHandler, dlgHandle, containerHandle);
                if (err != 0) 
                    return err;
            }
            else
                containerHandle = dlgHandle;
        }
        else
        {   //for versions 4.x, the license dialog contains a panel and it contains two textbox
            // then first get the handle of the panel
            containerHandle = FindWindowEx(dlgHandle,NULL,wxT("#32770"),NULL);

            //if the handle is NULL, this can may mean that the dialog handle can be of the Password Dialog (that doesn't contains a panel)
            // OR that the License Dialog was not finished drawing

            //to avoid the drawing posibility, waits a few milliseconds
            for (int count = 0; containerHandle == NULL && count < 6;count++)
            {
                thSleep(20,thread)
                containerHandle = FindWindowEx(dlgHandle,NULL,wxT("#32770"),NULL);
            }
            
            //if the handle of the container remains NULL, this means that the handle is for the Password Dialog, then enters the correct password in that dialog
            if (containerHandle == NULL) 
            {
                //the password dialog contains one textbox, if the handle of this textbox is null, what's happening? send error #3
                if (FindWindowEx(dlgHandle,0,wxT("Edit"),NULL) == NULL)
                {
                    SendMessage(dlgHandle, WM_IME_KEYDOWN, VK_ESCAPE, 0);
                    return 3;
                }

                int err = UnlockWithPass(nodHandler, dlgHandle, containerHandle);
                if (err != 0) 
                    return err;
            }
        }
        handles.Clear();

        dataHandle = FindWindowEx(containerHandle,0,wxT("Edit"),NULL);

        if (dataHandle == NULL) 
        {
            thSleep(10,thread)
            dataHandle = FindWindowEx(containerHandle,0,wxT("Edit"),NULL);

            if (dataHandle == NULL) 
            {
                SendMessage(dlgHandle, WM_IME_KEYDOWN, VK_ESCAPE, 0); //press ESC key to close the window
                return 3;
            }
        }
        
        //inserts the username in the textbox
        SendMessage(dataHandle,WM_SETTEXT,0,(LPARAM)license.GetUsername().c_str());
       
        dataHandle = FindWindowEx(containerHandle,dataHandle,wxT("Edit"),NULL);    

        if ( (m_EsetVersion.m_Major == 4 && m_EsetVersion.m_Minor >= 2) || m_EsetVersion.m_Major > 4)
        {
            SendMessage(dataHandle, EM_SETSEL, 0, 0x7fffffff);
            wxString pass2Insert = license.GetPassword();
            for (unsigned int c = 0; c < pass2Insert.Len(); c++)
                SendMessage(dataHandle, WM_CHAR, pass2Insert[c], 0x100001);
        }
        else
            SendMessage(dataHandle,WM_SETTEXT,0,(LPARAM)license.GetPassword().c_str());       

        //gets the handle of the first button ("ok" button) and press it
        dataHandle = FindWindowEx(containerHandle,dataHandle,wxT("Button"),NULL);
        PostMessage(dataHandle,BM_CLICK,0,0);

        if (m_RestoreActiveWindow == true)
            SetForegroundWindow(activeWindow);
    }
    else if (m_NodState == NOD_OLDVERSION) //version <= 2, we must use the registry directly
    {
        bool elevated = !IsVista() || (IsVista() && IsElevated() == S_OK);
        if (!elevated)
            return 7;
        else
            UpdatePath(&license);
    }
    bool inserted = WaitInsertion(license);
    if (inserted)
        return 0;
    else
        return 5;
}

// ---------------------------------------------------------------------------
// Waits to detect if the new username and password are saved in the registry
// ---------------------------------------------------------------------------
bool EsetManager::WaitInsertion(License& license)
{
    wxString binStrPass;
    License::EncodePass(license.GetPassword(),binStrPass);
    
    int sleepTime = 50;
    while((GetUsername() != license.GetUsername() || GetPassword() != binStrPass) && sleepTime <= 1350)
    {
        tkSleep(sleepTime)
        wxTheApp->ProcessPendingEvents();
        sleepTime*=3;
    }
    if (GetUsername() != license.GetUsername() || GetPassword() != binStrPass) // if the new license isn't in the registry, error
        return false;
    else 
        return true;
}

// ---------------------------------------------------------------------------
// Returns the current license used in ESET
// ---------------------------------------------------------------------------
void EsetManager::GetCurrentLicense(License &license)
{
    license.SetPassword(License::DecodePass(GetPassword()));
    license.SetUsername(GetUsername());
}